// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/ioctl.h>
#include <linux/poll.h>

#define DEVICE_NAME "fifo"
#define BUFFER_SIZE 1024
#define FIFO_CLEAR _IO('f', 0)

struct fifo_device {
    struct cdev device_cdev;
    struct class* device_class;
    char message[BUFFER_SIZE];
    int message_length;
    struct semaphore sem;
    wait_queue_head_t rd_wait;
    wait_queue_head_t wr_wait;
    int major_number;
    struct fasync_struct *async_queue; // 异步队列，用于支持异步编程
};

static struct fifo_device *fifo_dev = NULL;

static int fifo_fasync(int fd, struct file *filp, int mode);

static int device_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "fifo: Device opened\n");
    file->private_data = fifo_dev;
    return 0;
}

static int device_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "fifo: Device closed\n");
    // 释放异步节点
    fifo_fasync( -1, file, 0);
    return 0;
}

static ssize_t device_read(struct file *file, char __user *buffer, size_t len, loff_t *offset) {
    struct fifo_device *dev;
    int ret = 0;
    DECLARE_WAITQUEUE(wait, current);

    dev = (struct fifo_device *)file->private_data;

    down(&dev->sem);
    add_wait_queue(&dev->rd_wait, &wait);

    if (!dev->message_length) {
        if (file->f_flags & O_NONBLOCK) {
            ret = -EAGAIN;
            goto end;
        }

        __set_current_state(TASK_INTERRUPTIBLE);
        up(&dev->sem);
        schedule();
        if (signal_pending(current)) {
            ret = -ERESTARTSYS;
            goto end_nolock;
        }
        down(&dev->sem);
    }

    if (len > dev->message_length) {
        len = dev->message_length;
    }

    if (copy_to_user(buffer, dev->message, len)) {
        ret = -EFAULT;
        goto end;
    } else {
        memcpy(dev->message, dev->message + len, dev->message_length - len);
        dev->message_length -= len;
        pr_info("read %ld bytes, left:%d\n",
            len, dev->message_length);

        wake_up_interruptible(&dev->wr_wait); //����д�ȴ�����

        ret = len;
    }

end:
    up(&dev->sem);
end_nolock:
    remove_wait_queue(&dev->rd_wait, &wait);
    set_current_state(TASK_RUNNING);
    return ret;
}

static ssize_t device_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset) {
    struct fifo_device *dev;

    dev = (struct fifo_device *)file->private_data;
    int ret;
    DECLARE_WAITQUEUE(wait, current);

    down(&dev->sem);
    add_wait_queue(&dev->wr_wait, &wait);

    if (dev->message_length == BUFFER_SIZE) {
        if (file->f_flags &O_NONBLOCK) {
            ret = -EAGAIN;
            goto end;
        }
        __set_current_state(TASK_INTERRUPTIBLE);
        up(&dev->sem);

        schedule();
        if (signal_pending(current)) {
            ret =  - ERESTARTSYS;
            goto end_nolock;
        }

        down(&dev->sem);
    }

    if (len > BUFFER_SIZE - dev->message_length)
        len = BUFFER_SIZE - dev->message_length;

    if (copy_from_user(dev->message + dev->message_length, buffer, len)) {
        ret = -EFAULT;
        goto end;
    } else {
        dev->message_length += len;
        pr_info("written %ld bytes, lfet:%d\n",
                len, dev->message_length);

        wake_up_interruptible(&dev->rd_wait);

        /* 写入数据后，尝试唤醒异步队列，并且可以读取数据。 */
        if (dev->async_queue)
            kill_fasync(&dev->async_queue, SIGIO, POLL_IN);

        ret = len;
    }

end:
    up(&dev->sem);
end_nolock:
    remove_wait_queue(&dev->wr_wait, &wait);
    set_current_state(TASK_RUNNING);
    
    return ret;
}

static long fifo_unlocked_ioctl(
        struct file *filp,
        unsigned int cmd,
        unsigned long arg)
{
    struct fifo_device *dev = (struct fifo_device *)filp->private_data;

    switch (cmd) {
    case FIFO_CLEAR:
      	down(&dev->sem);
        dev->message_length = 0;
        memset(dev->message, 0, BUFFER_SIZE);
        up(&dev->sem);
        pr_info("fifo clear done\n");
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

static unsigned int fifo_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
    struct fifo_device *dev = (struct fifo_device *)filp->private_data;

    down(&dev->sem);

    poll_wait(filp, &dev->rd_wait, wait);
    poll_wait(filp, &dev->wr_wait, wait);

    if (dev->message_length != 0)
    {
        mask |= POLLIN | POLLRDNORM;
    }

    if (dev->message_length != BUFFER_SIZE)
    {
        mask |= POLLOUT | POLLWRNORM;
    }

    up(&dev->sem);
    return mask;
}

static int fifo_fasync(int fd, struct file *filp, int mode)
{
	struct fifo_device *dev = (struct fifo_device *)filp->private_data;
    // 调用异步辅助函数初始化队列
	return fasync_helper(fd, filp, mode, &dev->async_queue);
}

static struct file_operations fops = {
    .open = device_open,
    .release = device_release,
    .read = device_read,
    .write = device_write,
    .unlocked_ioctl = fifo_unlocked_ioctl,
    .poll = fifo_poll,
    .fasync = fifo_fasync,
};

static int __init simple_char_device_init(void) {
    // 动态分配一个主设备号
    int major_number = register_chrdev(0, DEVICE_NAME, &fops);
    if (major_number < 0) {
        printk(KERN_ALERT "fifo: Failed to register a major number\n");
        return major_number;
    }

    // 分配内存
    struct fifo_device *fifo = kmalloc(sizeof(struct fifo_device), GFP_KERNEL);
    if (!fifo) {
        unregister_chrdev(major_number, DEVICE_NAME);
        printk(KERN_ALERT "fifo: Failed to create device class\n");
        return -1;
    }

    // 创建一个设备类
    fifo->device_class = class_create(DEVICE_NAME);
    if (IS_ERR(fifo->device_class)) {
        kfree(fifo);
        unregister_chrdev(major_number, DEVICE_NAME);
        printk(KERN_ALERT "fifo: Failed to create device class\n");
        return PTR_ERR(fifo->device_class);
    }

    // 初始化cdev结构
    cdev_init(&fifo->device_cdev, &fops);
    if (cdev_add(&fifo->device_cdev, MKDEV(major_number, 0), 1) == -1) {
        class_destroy(fifo->device_class);
        unregister_chrdev(major_number, DEVICE_NAME);
        printk(KERN_ALERT "fifo: Failed to add cdev\n");
        return -1;
    }

    // 创建设备文件
    device_create(fifo->device_class, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME);

    memset(fifo->message, 0, sizeof(fifo->message));
    fifo->message_length = 0;
    sema_init(&fifo->sem, 1);
    init_waitqueue_head(&fifo->rd_wait);
    init_waitqueue_head(&fifo->wr_wait);
    fifo->major_number = major_number;

    fifo_dev = fifo;

    printk(KERN_INFO "fifo: Device class and device created\n");
    return 0;
}

static void __exit simple_char_device_exit(void) {
    if (!fifo_dev)
        return;
    device_destroy(fifo_dev->device_class, MKDEV(fifo_dev->major_number, 0));
    class_unregister(fifo_dev->device_class);
    class_destroy(fifo_dev->device_class);
    cdev_del(&fifo_dev->device_cdev);
    unregister_chrdev(fifo_dev->major_number, DEVICE_NAME);
    printk(KERN_INFO "fifo: Device removed\n");
}

module_init(simple_char_device_init);
module_exit(simple_char_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jason Hu");
MODULE_DESCRIPTION("A simple fifo device driver");
MODULE_VERSION("1.0");